﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace RingBufferStream.Tests
{
  /// <summary>
  ///This is a test class for RingBufferStreamTest and is intended
  ///to contain all RingBufferStreamTest Unit Tests
  ///</summary>
  [TestClass]
  public class RingBufferStreamTest
  {
    /// <summary>
    ///Gets or sets the test context which provides
    ///information about and functionality for the current test run.
    ///</summary>
    public TestContext TestContext { get; set; }

    #region Additional test attributes

    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    //[ClassInitialize()]
    //public static void MyClassInitialize(TestContext testContext)
    //{
    //}
    //
    //Use ClassCleanup to run code after all tests in a class have run
    //[ClassCleanup()]
    //public static void MyClassCleanup()
    //{
    //}
    //
    //Use TestInitialize to run code before running each test
    //[TestInitialize()]
    //public void MyTestInitialize()
    //{
    //}
    //
    //Use TestCleanup to run code after each test has run
    //[TestCleanup()]
    //public void MyTestCleanup()
    //{
    //}
    //

    #endregion

    /// <summary>
    ///A test for RingBufferStream Constructor
    ///</summary>
    [TestMethod]
    public void RingBufferStreamConstructorTest()
    {
      var target = new RingBufferStream(0);
      Assert.IsTrue(target.Capacity == 0);

      target = new RingBufferStream(1);
      Assert.IsTrue(target.Capacity == 1);
    }

    /// <summary>
    ///A test for GetBuffer
    ///</summary>
    [TestMethod]
    public void GetBufferTest()
    {
      var target = new RingBufferStream(256);
      IList<ArraySegment<byte>> expected = new List<ArraySegment<byte>>
        {
          new ArraySegment<byte>(target.GetBuffer(), 0, 0)
        };
      IList<ArraySegment<byte>> buffer = target.GetBuffer(0);
      Assert.IsNotNull(buffer);
      Assert.IsTrue(buffer.Count == 1);
      Assert.IsTrue(buffer[0].Count == 0);
      Assert.AreEqual(expected[0].Array, buffer[0].Array);

      for (int i = 0; i < 4; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
      }
      buffer = target.GetBuffer(16);
      Assert.IsNotNull(buffer);
      Assert.IsTrue(buffer.Count == 1);
      Assert.IsTrue(buffer[0].Count == 16);

      for (int i = 0; i < 4; ++i)
      {
        var bytes = new byte[4];
        target.Read(bytes, 0, 4);
        Assert.IsTrue(bytes.ByteArrayToStruct<int>(0, 4) == i);
      }

      for (int i = 0; i < 5; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
      }
      buffer = target.GetBuffer(20);
      Assert.IsNotNull(buffer);
      Assert.IsTrue(buffer.Count == 1);
      Assert.IsTrue(buffer[0].Count == 20);

      for (int i = 0; i < 48; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
        target.Write(new byte[1], 0, 1);
        target.Read(new byte[4], 0, 4);
      }
      target.Seek(-176, SeekOrigin.Current);
      buffer = target.GetBuffer((int)target.Length);
      Assert.IsNotNull(buffer);
      Assert.IsTrue(buffer.Count == 2);
      Assert.IsTrue(buffer[0].Offset == 32);
      Assert.IsTrue(buffer[0].Count == 224);
      Assert.IsTrue(buffer[1].Offset == 0);
      Assert.IsTrue(buffer[1].Count == 20);
    }

    /// <summary>
    ///A test for Read
    ///</summary>
    [TestMethod]
    public void ReadTest()
    {
      var target = new RingBufferStream(64);

      var buffer = new byte[256];
      int actual = target.Read(buffer, 0, 0);
      Assert.AreEqual(0, actual);

      int i;
      for (i = 0; i < 16; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
      }
      Assert.IsTrue(target.Length == 64);
      Assert.IsTrue(target.Capacity == 64);

      for (i = 0; i < 15; ++i)
      {
        actual = target.Read(buffer, i * 4, 4);
        Assert.AreEqual(4, actual);
        Assert.IsTrue(buffer.ByteArrayToStruct<int>(i * 4, 4) == i);
      }
      Assert.IsTrue(target.Length == 4);
      Assert.IsTrue(target.Capacity == 64);

      actual = target.Read(buffer, i * 4, 8);
      Assert.AreEqual(4, actual);
      Assert.IsTrue(buffer.ByteArrayToStruct<int>(i * 4, 4) == i);
      Assert.IsTrue(target.Length == 0);
      Assert.IsTrue(target.Capacity == 64);

      for (int j = 1; j <= 10; ++j)
      {
        for (i = 0; i < 16 * j; ++i)
        {
          target.Write(i.StructToByteArray(), 0, 4);
          target.WriteByte(0);
        }
        Assert.IsTrue(target.Length == i * 5);
        Assert.IsTrue(target.Capacity >= 64 * j);

        target.GetBuffer(i * 5);

        for (i = 0; i < 16 * j; ++i)
        {
          actual = target.Read(buffer, 0, 4);
          Assert.AreEqual(4, actual);
          Assert.IsTrue(buffer.ByteArrayToStruct<int>(0, 4) == i);
          Assert.IsTrue(target.ReadByte() == 0);
        }
        Assert.IsTrue(target.Length == 0);
        Assert.IsTrue(target.Capacity >= 64 * j);
      }
    }

    [TestMethod]
    public void ReadTest2()
    {
      var target = new RingBufferStream(12);
      var write = new[] {(byte)'1', (byte)'2', (byte)'3', (byte)'4'};
      target.Write(write, 0, 4);

      var read = new byte[4];
      int count = target.Read(read, 0, 4);
      Assert.AreEqual(4, count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], read[i]);
      }

      target.Write(write, 0, 4);
      count = target.Read(read, 0, 4);
      Assert.AreEqual(4, count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], read[i]);
      }

      target.Write(write, 0, 4);
      count = target.Read(read, 0, 4);
      Assert.AreEqual(4, count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], read[i]);
      }

      target.Write(write, 0, 4);
      count = target.Read(read, 0, 4);
      Assert.AreEqual(4, count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], read[i]);
      }
    }

    [TestMethod]
    public void SeekTest()
    {
      var target = new RingBufferStream(64);

      var buffer = new byte[256];
      long actual = target.Read(buffer, 0, 0);
      Assert.AreEqual(0, actual);

      int i;
      for (i = 0; i < 16; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
      }
      Assert.IsTrue(target.Length == 64);
      Assert.IsTrue(target.Capacity == 64);

      for (i = 0; i < 15; ++i)
      {
        Assert.IsTrue(target.GetBuffer(4).ByteArrayToStruct<int>(4) == i);
        actual = target.Seek(4, SeekOrigin.Current);
        Assert.AreEqual((i + 1)*4, actual);
      }
      Assert.IsTrue(target.Length == 4);
      Assert.IsTrue(target.Capacity == 64);

      Assert.IsTrue(target.GetBuffer(4).ByteArrayToStruct<int>(4) == i);
      actual = target.Seek(4, SeekOrigin.Current);
      Assert.AreEqual(64, actual);
      Assert.IsTrue(target.Length == 0);
      Assert.IsTrue(target.Capacity == 64);

      for (int j = 1; j <= 10; ++j)
      {
        for (i = 0; i < 16 * j; ++i)
        {
          target.Write(i.StructToByteArray(), 0, 4);
          target.WriteByte(0);
        }
        Assert.IsTrue(target.Length == i * 5);
        Assert.IsTrue(target.Capacity >= 64 * j);

        target.GetBuffer(i * 5);

        for (i = 0; i < 16 * j; ++i)
        {
          Assert.IsTrue(target.GetBuffer(4).ByteArrayToStruct<int>(4) == i);
          target.Seek(4, SeekOrigin.Current);
          Assert.IsTrue(target.GetBuffer(1).ByteArrayToStruct<byte>(1) == 0);
          target.Seek(1, SeekOrigin.Current);
        }
        Assert.IsTrue(target.Length == 0);
        Assert.IsTrue(target.Capacity >= 64 * j);
      }
    }

    [TestMethod]
    public void SeekTest2()
    {
      var target = new RingBufferStream(12);
      var write = new[] { (byte)'1', (byte)'2', (byte)'3', (byte)'4' };
      target.Write(write, 0, 4);

      IList<ArraySegment<byte>> buffer = target.GetBuffer(4);
      Assert.IsTrue(buffer.Count == 1);
      Assert.AreEqual(4, buffer[0].Count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], buffer[0].Array[i]);
      }

      target.Seek(4, SeekOrigin.Current);
      target.Write(write, 0, 4);
      buffer = target.GetBuffer(4);
      Assert.IsTrue(buffer.Count == 1);
      Assert.AreEqual(4, buffer[0].Count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], buffer[0].Array[i]);
      }

      target.Seek(4, SeekOrigin.Current);
      target.Write(write, 0, 4);
      buffer = target.GetBuffer(4);
      Assert.IsTrue(buffer.Count == 1);
      Assert.AreEqual(4, buffer[0].Count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], buffer[0].Array[i]);
      }

      target.Seek(4, SeekOrigin.Current);
      target.Write(write, 0, 4);
      buffer = target.GetBuffer(4);
      Assert.IsTrue(buffer.Count == 1);
      Assert.AreEqual(4, buffer[0].Count);
      for (int i = 0; i < 4; i++)
      {
        Assert.AreEqual(write[i], buffer[0].Array[i]);
      }

      target.Seek(4, SeekOrigin.Current);
    }

    /// <summary>
    ///A test for ReadByte
    ///</summary>
    [TestMethod]
    public void ReadByteTest()
    {
      var target = new RingBufferStream(256);
      int actual = target.ReadByte();
      Assert.AreEqual(-1, actual);
    }

    /// <summary>
    ///A test for SetLength
    ///</summary>
    [TestMethod]
    public void SetLengthTest()
    {
      var target = new RingBufferStream(256);
      target.SetLength(300);
      Assert.IsTrue(target.Capacity == 1024);
    }

    /// <summary>
    ///A test for ToArray
    ///</summary>
    [TestMethod]
    public void ToArrayTest()
    {
      var target = new RingBufferStream(256);
      byte[] actual = target.ToArray();
      Assert.IsNotNull(actual);
      Assert.IsTrue(target.Length == 0);
    }

    /// <summary>
    ///A test for Write
    ///</summary>
    [TestMethod]
    public void WriteTest()
    {
      var target = new RingBufferStream(64);
      target.Write(new byte[0], 0, 0);
      Assert.IsTrue(target.Length == 0);

      int i;
      for (i = 0; i < 15; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
      }
      Assert.IsTrue(target.Length == 60);
      Assert.IsTrue(target.Capacity == 64);

      for (i = 0; i < 12; ++i)
      {
        var buffer = new byte[4];
        target.Read(buffer, 0, 4);
        Assert.IsTrue(buffer.ByteArrayToStruct<int>(0, 4) == i);
      }
      Assert.IsTrue(target.Length == 12);
      Assert.IsTrue(target.Capacity == 64);

      for (i = 16; i < 32; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
        target.Write(new byte[1], 0, 1);
      }
      Assert.IsTrue(target.Length == 92);
      Assert.IsTrue(target.Capacity == 1024);

      for (i = 12; i < 32; ++i)
      {
        if (i == 15) continue;
        var buffer = new byte[5];
        target.Read(buffer, 0, i > 15 ? 5 : 4);
        Assert.IsTrue(buffer.ByteArrayToStruct<int>(0, 4) == i);
        if (i > 15)
          Assert.IsTrue(buffer[4] == 0);
      }
      Assert.IsTrue(target.Length == 0);

      for (i = 0; i < 52; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
        target.Write(new byte[1], 0, 1);
      }
      Assert.IsTrue(target.Length == 260);
      Assert.IsTrue(target.Capacity == 1024);

      for (i = 0; i < 52; ++i)
      {
        var buffer = new byte[5];
        target.Read(buffer, 0, 5);
        Assert.IsTrue(buffer.ByteArrayToStruct<int>(0, 4) == i);
        Assert.IsTrue(buffer[4] == 0);
      }
      Assert.IsTrue(target.Length == 0);

      for (i = 0; i < 104; ++i)
      {
        target.Write(i.StructToByteArray(), 0, 4);
        target.Write(new byte[1], 0, 1);
      }
      Assert.IsTrue(target.Length == 520);
      Assert.IsTrue(target.Capacity == 1024);

      for (i = 0; i < 104; ++i)
      {
        var buffer = new byte[5];
        target.Read(buffer, 0, 5);
        Assert.IsTrue(buffer.ByteArrayToStruct<int>(0, 4) == i);
        Assert.IsTrue(buffer[4] == 0);
      }
      Assert.IsTrue(target.Length == 0);
      Assert.IsTrue(target.ReadByte() == -1);
      Assert.IsTrue(target.Read(new byte[5], 0, 5) == 0);
    }

    /// <summary>
    ///A test for WriteByte
    ///</summary>
    [TestMethod]
    public void WriteByteTest()
    {
      var target = new RingBufferStream(256);
      target.WriteByte(0);
      Assert.IsTrue(target.Length == 1);
    }

    /// <summary>
    ///A test for Length
    ///</summary>
    [TestMethod]
    public void LengthTest()
    {
      var target = new RingBufferStream(256);
      Assert.IsTrue(target.Length == 0);
    }
  }
}